﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Timers;
using System.IO;
using System.ComponentModel;

namespace CleanBackupDLL
{
	public class Backup
	{
		public const double INTERVAL = 60;

		public delegate void ChangedStatusEventHandler(object sender, EventArgs e);
		public event ChangedStatusEventHandler ChangedStatus;

		public delegate void ChangedProgressEventHandler(object sender, EventArgs e);
		public event ChangedProgressEventHandler ChangedProgress;

		Timer timer;
		BackgroundWorker bgWorker;
		BackupStatus backupStatus;

		public Backup()
		{
			bgWorker = new BackgroundWorker();
			bgWorker.WorkerSupportsCancellation = true;
			bgWorker.DoWork += new DoWorkEventHandler(bgWorker_DoWork);
			bgWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgWorker_RunWorkerCompleted);

			timer = new Timer(1000);
			timer.AutoReset = true;
			timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
		}

		void timer_Elapsed(object sender, ElapsedEventArgs e)
		{
			if (DateTime.Now > backupStatus.NextBackupTime)
				StartBackup();
		}

		void bgWorker_DoWork(object sender, DoWorkEventArgs e)
		{
			timer.Enabled = false;

			Copy();
		}

		void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
		{
			Reset();
		}

		public void StartBackup()
		{
			bgWorker.RunWorkerAsync();
		}

		public void Initialize()
		{
			backupStatus = Configuration.Load();
			SetTimer(backupStatus.IsTimerRunning);

			OnChangedStatus();
		}

		int Copy()
		{
			if (!Directory.Exists(backupStatus.SrcPath) || !Directory.Exists(backupStatus.DestPath))
			{
				backupStatus.State = States.ERROR;
				return 1;
			}

			List<FileInfo> fileList = new DirectoryInfo(backupStatus.SrcPath).GetFiles("*", SearchOption.AllDirectories).Where(e => e.Attributes == FileAttributes.Archive).ToList();

			backupStatus.NextBackupTime = DateTime.MinValue;
			backupStatus.TotalFiles = fileList.Count;
			if (backupStatus.TotalFiles == 0)
			{
				backupStatus.State = States.FINISHED;
			}
			else
			{
				backupStatus.State = States.RUNNING;
				OnChangedStatus();

				Progress progress = new Progress();
				int pathLength = backupStatus.SrcPath.Length;

				foreach (FileInfo file in fileList)
				{
					if (bgWorker.CancellationPending)
						return 1;

					string targetPath = backupStatus.DestPath + file.DirectoryName.Substring(pathLength);
					try
					{
						Directory.CreateDirectory(targetPath);
						File.Copy(file.FullName, targetPath + "\\" + file.Name, true);
						file.Attributes = FileAttributes.Normal;
						progress.successFiles++;
					}
					catch (UnauthorizedAccessException)
					{
						progress.errorFiles++;
					}
					catch (IOException)
					{
						progress.errorFiles++;
					}

					OnChangedProgress(progress);
				}

				backupStatus.State = States.FINISHED;
			}

			return 0;
		}

		void CalculateNextBackup()
		{
			if (backupStatus.IsTimerRunning)
				backupStatus.NextBackupTime = DateTime.Now.AddMinutes(INTERVAL);
		}

		public void SetTimer(bool timerOn)
		{
			backupStatus.IsTimerRunning = timerOn;
			if (backupStatus.State != States.RUNNING)
			{
				timer.Enabled = backupStatus.IsTimerRunning;
				backupStatus.State = backupStatus.IsTimerRunning ? States.WAITING : States.STOPPED;

				CalculateNextBackup();
			}

			OnChangedStatus();
		}

		public void SetPath(string srcPath, string destPath)
		{
			backupStatus.SrcPath = srcPath;
			backupStatus.DestPath = destPath;
		}

		public void Cancel()
		{
			if (backupStatus.State == States.RUNNING)
				bgWorker.CancelAsync();
		}

		public void Save()
		{
			Configuration.Save(backupStatus);
		}

		void Reset()
		{
			//backupStatus.TotalFiles = 0;

			SetTimer(backupStatus.IsTimerRunning);
			OnChangedStatus();
			//OnChangedProgress(new Progress());
		}

		protected void OnChangedStatus()
		{
			if (ChangedStatus != null)
				ChangedStatus(backupStatus, backupStatus);
		}

		protected void OnChangedProgress(Progress e)
		{
			if (ChangedProgress != null)
				ChangedProgress(backupStatus, e);
		}
	}
}
